home *** CD-ROM | disk | FTP | other *** search
/ Aminet 28 / Aminet 28 (1998)(GTI - Schatztruhe)[!][Dec 1998].iso / Aminet / dev / c / qtools0.2-src.lha / src / libqtools / BMP.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-07-18  |  24.9 KB  |  1,164 lines

  1. /*\
  2.  * $Id: bmp.h,v 1.3 1992/11/24 19:39:56 dws Exp dws $
  3.  * 
  4.  * bmp.h - routines to calculate sizes of parts of BMP files
  5.  *
  6.  * Some fields in BMP files contain offsets to other parts
  7.  * of the file.  These routines allow us to calculate these
  8.  * offsets, so that we can read and write BMP files without
  9.  * the need to fseek().
  10.  * 
  11.  * Copyright (C) 1992 by David W. Sanderson.
  12.  * 
  13.  * Permission to use, copy, modify, and distribute this software and its
  14.  * documentation for any purpose and without fee is hereby granted,
  15.  * provided that the above copyright notice appear in all copies and
  16.  * that both that copyright notice and this permission notice appear
  17.  * in supporting documentation.  This software is provided "as is"
  18.  * without express or implied warranty.
  19.  * 
  20.  * $Log: bmp.h,v $
  21.  * Revision 1.3  1992/11/24  19:39:56  dws
  22.  * Added copyright.
  23.  *
  24.  * Revision 1.2  1992/11/17  02:13:37  dws
  25.  * Adjusted a string's name.
  26.  *
  27.  * Revision 1.1  1992/11/16  19:54:44  dws
  28.  * Initial revision
  29.  *
  30.  \*/
  31.  
  32. /* prototypes */
  33. static unsigned long BMPlenfileheader ARGS((int class));
  34. static unsigned long BMPleninfoheader ARGS((int class));
  35. static unsigned long BMPlenrgbtable ARGS((int class, unsigned long bitcount));
  36. static unsigned long BMPlenline ARGS((int class, unsigned long bitcount, unsigned long x));
  37. static unsigned long BMPlenbits ARGS((int class, unsigned long bitcount, unsigned long x, unsigned long y));
  38. static unsigned long BMPlenfile ARGS((int class, unsigned long bitcount, unsigned long x, unsigned long y));
  39. static unsigned long BMPoffbits ARGS((int class, unsigned long bitcount));
  40.  
  41. /*
  42.  * Classes of BMP files
  43.  */
  44.  
  45. #define C_WIN    1
  46. #define C_OS2    2
  47.  
  48. static char er_internal[] = "%s: internal error!";
  49.  
  50. static unsigned long BMPlenfileheader(class)
  51.     int class;
  52. {
  53.   switch (class) {
  54.     case C_WIN:
  55.       return 14;
  56.     case C_OS2:
  57.       return 14;
  58.     default:
  59.       pm_error(er_internal, "BMPlenfileheader");
  60.       return 0;
  61.   }
  62. }
  63.  
  64. static unsigned long BMPleninfoheader(class)
  65.     int class;
  66. {
  67.   switch (class) {
  68.     case C_WIN:
  69.       return 40;
  70.     case C_OS2:
  71.       return 12;
  72.     default:
  73.       pm_error(er_internal, "BMPleninfoheader");
  74.       return 0;
  75.   }
  76. }
  77.  
  78. static unsigned long BMPlenrgbtable(class, bitcount)
  79.     int class;
  80.     unsigned long bitcount;
  81. {
  82.   unsigned long lenrgb;
  83.  
  84.   if (bitcount < 1) {
  85.     pm_error(er_internal, "BMPlenrgbtable");
  86.     return 0;
  87.   }
  88.   switch (class) {
  89.     case C_WIN:
  90.       lenrgb = 4;
  91.       break;
  92.     case C_OS2:
  93.       lenrgb = 3;
  94.       break;
  95.     default:
  96.       pm_error(er_internal, "BMPlenrgbtable");
  97.       return 0;
  98.   }
  99.  
  100.   return (1 << bitcount) * lenrgb;
  101. }
  102.  
  103. /*
  104.  * length, in bytes, of a line of the image
  105.  * 
  106.  * Evidently each row is padded on the right as needed to make it a
  107.  * multiple of 4 bytes long.  This appears to be true of both
  108.  * OS/2 and Windows BMP files.
  109.  */
  110. static unsigned long BMPlenline(class, bitcount, x)
  111.     int class;
  112.     unsigned long bitcount;
  113.     unsigned long x;
  114. {
  115.   unsigned long bitsperline;
  116.  
  117.   switch (class) {
  118.     case C_WIN:
  119.       break;
  120.     case C_OS2:
  121.       break;
  122.     default:
  123.       pm_error(er_internal, "BMPlenline");
  124.       return 0;
  125.   }
  126.  
  127.   bitsperline = x * bitcount;
  128.  
  129.   /*
  130.    * if bitsperline is not a multiple of 32, then round
  131.    * bitsperline up to the next multiple of 32.
  132.    */
  133.   if ((bitsperline % 32) != 0) {
  134.     bitsperline += (32 - (bitsperline % 32));
  135.   }
  136.  
  137.   if ((bitsperline % 32) != 0) {
  138.     pm_error(er_internal, "BMPlenline");
  139.     return 0;
  140.   }
  141.  
  142.   /* number of bytes per line == bitsperline/8 */
  143.   return bitsperline >> 3;
  144. }
  145.  
  146. /* return the number of bytes used to store the image bits */
  147. static unsigned long BMPlenbits(class, bitcount, x, y)
  148.     int class;
  149.     unsigned long bitcount;
  150.     unsigned long x;
  151.     unsigned long y;
  152. {
  153.   return y * BMPlenline(class, bitcount, x);
  154. }
  155.  
  156. /* return the offset to the BMP image bits */
  157. static unsigned long BMPoffbits(class, bitcount)
  158.     int class;
  159.     unsigned long bitcount;
  160. {
  161.   return BMPlenfileheader(class)
  162.     + BMPleninfoheader(class)
  163.     + BMPlenrgbtable(class, bitcount);
  164. }
  165.  
  166. /* return the size of the BMP file in bytes */
  167. static unsigned long BMPlenfile(class, bitcount, x, y)
  168.     int class;
  169.     unsigned long bitcount;
  170.     unsigned long x;
  171.     unsigned long y;
  172. {
  173.   return BMPoffbits(class, bitcount)
  174.     + BMPlenbits(class, bitcount, x, y);
  175. }
  176.  
  177. /*\
  178.  * $Id: bmptoppm.c,v 1.10 1992/11/24 19:38:17 dws Exp dws $
  179.  * 
  180.  * bmptoppm.c - Converts from a Microsoft Windows or OS/2 .BMP file to a
  181.  * PPM file.
  182.  * 
  183.  * The current implementation is probably not complete, but it works for
  184.  * all the BMP files I have.  I welcome feedback.
  185.  * 
  186.  * Copyright (C) 1992 by David W. Sanderson.
  187.  * 
  188.  * Permission to use, copy, modify, and distribute this software and its
  189.  * documentation for any purpose and without fee is hereby granted,
  190.  * provided that the above copyright notice appear in all copies and
  191.  * that both that copyright notice and this permission notice appear
  192.  * in supporting documentation.  This software is provided "as is"
  193.  * without express or implied warranty.
  194.  * 
  195.  * $Log: bmptoppm.c,v $
  196.  * Revision 1.10  1992/11/24  19:38:17  dws
  197.  * Added code to verify that reading occurred at the correct offsets.
  198.  * Added copyright.
  199.  *
  200.  * Revision 1.9  1992/11/17  02:15:24  dws
  201.  * Changed to include bmp.h.
  202.  * Eliminated need for fseek(), and therefore the need for a
  203.  * temporary file.
  204.  *
  205.  * Revision 1.8  1992/11/13  23:48:57  dws
  206.  * Made definition of Seekable() static, to match its prototype.
  207.  *
  208.  * Revision 1.7  1992/11/11  00:17:50  dws
  209.  * Generalized to use bitio routines.
  210.  *
  211.  * Revision 1.6  1992/11/10  23:51:44  dws
  212.  * Enhanced command-line handling.
  213.  *
  214.  * Revision 1.5  1992/11/08  00:38:46  dws
  215.  * Changed some names to help w/ addition of ppmtobmp.
  216.  *
  217.  * Revision 1.4  1992/10/27  06:28:28  dws
  218.  * Corrected stupid typo.
  219.  *
  220.  * Revision 1.3  1992/10/27  06:17:10  dws
  221.  * Removed a magic constant value.
  222.  *
  223.  * Revision 1.2  1992/10/27  06:09:58  dws
  224.  * Made stdin seekable.
  225.  *
  226.  * Revision 1.1  1992/10/27  05:31:41  dws
  227.  * Initial revision
  228.  \*/
  229.  
  230. #define    MAXCOLORS       256
  231.  
  232. static char *ifname;
  233.  
  234. /*
  235.  * Utilities
  236.  */
  237.  
  238. static int GetByte ARGS((FILE * fp));
  239. static short GetShort ARGS((FILE * fp));
  240. static long GetLong ARGS((FILE * fp));
  241. static void readto ARGS((FILE * fp, unsigned long *ppos, unsigned long dst));
  242. static void BMPreadfileheader ARGS((FILE * fp, unsigned long *ppos,
  243.                     unsigned long *poffBits));
  244. static void BMPreadinfoheader ARGS((FILE * fp, unsigned long *ppos,
  245.        unsigned long *pcx, unsigned long *pcy, unsigned short *pcBitCount,
  246.                     int *pclass));
  247. static int BMPreadrgbtable ARGS((FILE * fp, unsigned long *ppos,
  248.     unsigned short cBitCount, int class, pixval * R, pixval * G, pixval * B));
  249. static int BMPreadrow ARGS((FILE * fp, unsigned long *ppos, pixel * row,
  250.                 unsigned long cx, unsigned short cBitCount, pixval * R, pixval * G, pixval * B));
  251. static pixel **BMPreadbits ARGS((FILE * fp, unsigned long *ppos,
  252.             unsigned long offBits, unsigned long cx, unsigned long cy,
  253.     unsigned short cBitCount, int class, pixval * R, pixval * G, pixval * B));
  254.  
  255. static char er_read[] = "%s: read error";
  256. static char er_seek[] = "%s: seek error";
  257.  
  258. static int GetByte(fp)
  259.     FILE *fp;
  260. {
  261.   int v;
  262.  
  263.   if ((v = getc(fp)) == EOF) {
  264.     pm_error(er_read, ifname);
  265.   }
  266.  
  267.   return v;
  268. }
  269.  
  270. static short GetShort(fp)
  271.     FILE *fp;
  272. {
  273.   short v;
  274.  
  275.   if (pm_readlittleshort(fp, &v) == -1) {
  276.     pm_error(er_read, ifname);
  277.   }
  278.  
  279.   return v;
  280. }
  281.  
  282. static long GetLong(fp)
  283.     FILE *fp;
  284. {
  285.   long v;
  286.  
  287.   if (pm_readlittlelong(fp, &v) == -1) {
  288.     pm_error(er_read, ifname);
  289.   }
  290.  
  291.   return v;
  292. }
  293.  
  294. /*
  295.  * readto - read as many bytes as necessary to position the
  296.  * file at the desired offset.
  297.  */
  298.  
  299. static void readto(fp, ppos, dst)
  300.     FILE *fp;
  301.     unsigned long *ppos;                    /* pointer to number of bytes read from fp */
  302.     unsigned long dst;
  303. {
  304.   unsigned long pos;
  305.  
  306.   if (!fp || !ppos)
  307.     return;
  308.  
  309.   pos = *ppos;
  310.  
  311.   if (pos > dst)
  312.     pm_error("%s: internal error in readto()", ifname);
  313.  
  314.   for (; pos < dst; pos++) {
  315.     if (getc(fp) == EOF) {
  316.       pm_error(er_read, ifname);
  317.     }
  318.   }
  319.  
  320.   *ppos = pos;
  321. }
  322.  
  323. /*
  324.  * BMP reading routines
  325.  */
  326.  
  327. static void BMPreadfileheader(fp, ppos, poffBits)
  328.     FILE *fp;
  329.     unsigned long *ppos;                    /* number of bytes read from fp */
  330.     unsigned long *poffBits;
  331. {
  332.   unsigned long cbSize;
  333.   unsigned short xHotSpot;
  334.   unsigned short yHotSpot;
  335.   unsigned long offBits;
  336.  
  337.   if (GetByte(fp) != 'B') {
  338.     pm_error("%s is not a BMP file", ifname);
  339.   }
  340.   if (GetByte(fp) != 'M') {
  341.     pm_error("%s is not a BMP file", ifname);
  342.   }
  343.  
  344.   cbSize = GetLong(fp);
  345.   xHotSpot = GetShort(fp);
  346.   yHotSpot = GetShort(fp);
  347.   offBits = GetLong(fp);
  348.  
  349.   *poffBits = offBits;
  350.  
  351.   *ppos += 14;
  352. }
  353.  
  354. static void BMPreadinfoheader(fp, ppos, pcx, pcy, pcBitCount, pclass)
  355.     FILE *fp;
  356.     unsigned long *ppos;                    /* number of bytes read from fp */
  357.     unsigned long *pcx;
  358.     unsigned long *pcy;
  359.     unsigned short *pcBitCount;
  360.     int *pclass;
  361. {
  362.   unsigned long cbFix;
  363.   unsigned short cPlanes;
  364.  
  365.   unsigned long cx;
  366.   unsigned long cy;
  367.   unsigned short cBitCount;
  368.   int class;
  369.  
  370.   cbFix = GetLong(fp);
  371.  
  372.   switch (cbFix) {
  373.     case 12:
  374.       class = C_OS2;
  375.  
  376.       cx = GetShort(fp);
  377.       cy = GetShort(fp);
  378.       cPlanes = GetShort(fp);
  379.       cBitCount = GetShort(fp);
  380.  
  381.       break;
  382.     case 40:
  383.       class = C_WIN;
  384.  
  385.       cx = GetLong(fp);
  386.       cy = GetLong(fp);
  387.       cPlanes = GetShort(fp);
  388.       cBitCount = GetShort(fp);
  389.  
  390.       /*
  391.        * We've read 16 bytes so far, need to read 24 more
  392.        * for the required total of 40.
  393.        */
  394.  
  395.       GetLong(fp);
  396.       GetLong(fp);
  397.       GetLong(fp);
  398.       GetLong(fp);
  399.       GetLong(fp);
  400.       GetLong(fp);
  401.  
  402.       break;
  403.     default:
  404.       pm_error("%s: unknown cbFix: %d", ifname, cbFix);
  405.       break;
  406.   }
  407.  
  408.   if (cPlanes != 1) {
  409.     pm_error("%s: don't know how to handle cPlanes = %d"
  410.          ,ifname
  411.          ,cPlanes);
  412.   }
  413.  
  414.   switch (class) {
  415.     case C_WIN:
  416.       pm_message("Windows BMP, %dx%dx%d"
  417.          ,cx
  418.          ,cy
  419.          ,cBitCount);
  420.       break;
  421.     case C_OS2:
  422.       pm_message("OS/2 BMP, %dx%dx%d"
  423.          ,cx
  424.          ,cy
  425.          ,cBitCount);
  426.       break;
  427.   }
  428.  
  429.   *pcx = cx;
  430.   *pcy = cy;
  431.   *pcBitCount = cBitCount;
  432.   *pclass = class;
  433.  
  434.   *ppos += cbFix;
  435. }
  436.  
  437. /*
  438.  * returns the number of bytes read, or -1 on error.
  439.  */
  440. static int BMPreadrgbtable(fp, ppos, cBitCount, class, R, G, B)
  441.     FILE *fp;
  442.     unsigned long *ppos;                    /* number of bytes read from fp */
  443.     unsigned short cBitCount;
  444.     int class;
  445.     pixval *R;
  446.     pixval *G;
  447.     pixval *B;
  448. {
  449.   int i;
  450.   int nbyte = 0;
  451.  
  452.   long ncolors = (1 << cBitCount);
  453.  
  454.   for (i = 0; i < ncolors; i++) {
  455.     B[i] = (pixval) GetByte(fp);
  456.     G[i] = (pixval) GetByte(fp);
  457.     R[i] = (pixval) GetByte(fp);
  458.     nbyte += 3;
  459.  
  460.     if (class == C_WIN) {
  461.       (void)GetByte(fp);
  462.       nbyte++;
  463.     }
  464.   }
  465.  
  466.   *ppos += nbyte;
  467.   return nbyte;
  468. }
  469.  
  470. /*
  471.  * returns the number of bytes read, or -1 on error.
  472.  */
  473. static int BMPreadrow(fp, ppos, row, cx, cBitCount, R, G, B)
  474.     FILE *fp;
  475.     unsigned long *ppos;                    /* number of bytes read from fp */
  476.     pixel *row;
  477.     unsigned long cx;
  478.     unsigned short cBitCount;
  479.     pixval *R;
  480.     pixval *G;
  481.     pixval *B;
  482. {
  483.   BITSTREAM b;
  484.   unsigned nbyte = 0;
  485.   int rc;
  486.   unsigned x;
  487.  
  488.   if ((b = pm_bitinit(fp, "r")) == (BITSTREAM) 0) {
  489.     return -1;
  490.   }
  491.  
  492.   for (x = 0; x < cx; x++, row++) {
  493.     unsigned long v;
  494.  
  495.     if ((rc = pm_bitread(b, cBitCount, &v)) == -1) {
  496.       return -1;
  497.     }
  498.     nbyte += rc;
  499.  
  500.     PPM_ASSIGN(*row, R[v], G[v], B[v]);
  501.   }
  502.  
  503.   if ((rc = pm_bitfini(b)) != 0) {
  504.     return -1;
  505.   }
  506.  
  507.   /*
  508.    * Make sure we read a multiple of 4 bytes.
  509.    */
  510.   while (nbyte % 4) {
  511.     GetByte(fp);
  512.     nbyte++;
  513.   }
  514.  
  515.   *ppos += nbyte;
  516.   return nbyte;
  517. }
  518.  
  519. static pixel **
  520.   BMPreadbits(fp, ppos, offBits, cx, cy, cBitCount, class, R, G, B)
  521.     FILE *fp;
  522.     unsigned long *ppos;                    /* number of bytes read from fp */
  523.     unsigned long offBits;
  524.     unsigned long cx;
  525.     unsigned long cy;
  526.     unsigned short cBitCount;
  527.     int class;
  528.     pixval *R;
  529.     pixval *G;
  530.     pixval *B;
  531. {
  532.   pixel **pixels;                        /* output */
  533.   long y;
  534.  
  535.   readto(fp, ppos, offBits);
  536.  
  537.   pixels = ppm_allocarray(cx, cy);
  538.  
  539.   if (cBitCount > 24) {
  540.     pm_error("%s: cannot handle cBitCount: %d"
  541.          ,ifname
  542.          ,cBitCount);
  543.   }
  544.  
  545.   /*
  546.    * The picture is stored bottom line first, top line last
  547.    */
  548.  
  549.   for (y = cy - 1; y >= 0; y--) {
  550.     int rc;
  551.  
  552.     rc = BMPreadrow(fp, ppos, pixels[y], cx, cBitCount, R, G, B);
  553.  
  554.     if (rc == -1) {
  555.       pm_error("%s: couldn't read row %d"
  556.            ,ifname
  557.            ,y);
  558.     }
  559.     if (rc % 4) {
  560.       pm_error("%s: row had bad number of bytes: %d"
  561.            ,ifname
  562.            ,rc);
  563.     }
  564.   }
  565.  
  566.   return pixels;
  567. }
  568.  
  569. int main(argc, argv)
  570.     int argc;
  571.     char **argv;
  572. {
  573.   FILE *ifp = stdin;
  574.   char *usage = "[bmpfile]";
  575.   int argn;
  576.  
  577.   int rc;
  578.   unsigned long pos = 0;
  579.  
  580.   unsigned long offBits;
  581.  
  582.   unsigned long cx;
  583.   unsigned long cy;
  584.   unsigned short cBitCount;
  585.   int class;
  586.  
  587.   pixval R[MAXCOLORS];                        /* reds */
  588.   pixval G[MAXCOLORS];                        /* greens */
  589.   pixval B[MAXCOLORS];                        /* blues */
  590.  
  591.   pixel **pixels;
  592.  
  593.   ppm_init(&argc, argv);
  594.  
  595.   /*
  596.    * Since this command takes no flags, produce an error message
  597.    * if the user tries to give any.
  598.    * This is friendlier than if the command were to say
  599.    * 'no such file: -help'.
  600.    */
  601.  
  602.   argn = 1;
  603.   while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
  604.     pm_usage(usage);
  605.     ++argn;
  606.   }
  607.  
  608.   if (argn < argc) {
  609.     ifname = argv[argn];
  610.     ifp = pm_openr(ifname);
  611.     ++argn;
  612.   }
  613.   else {
  614.     ifname = "standard input";
  615.     ifp = stdin;
  616.   }
  617.  
  618.   if (argn != argc) {
  619.     pm_usage(usage);
  620.   }
  621.  
  622.   BMPreadfileheader(ifp, &pos, &offBits);
  623.   BMPreadinfoheader(ifp, &pos, &cx, &cy, &cBitCount, &class);
  624.  
  625.   if (offBits != BMPoffbits(class, cBitCount)) {
  626.     pm_message("warning: offBits is %d, expected %d"
  627.            ,pos
  628.            ,BMPoffbits(class, cBitCount));
  629.   }
  630.  
  631.   rc = BMPreadrgbtable(ifp, &pos, cBitCount, class, R, G, B);
  632.  
  633.   if (rc != BMPlenrgbtable(class, cBitCount)) {
  634.     pm_message("warning: %d-byte RGB table, expected %d bytes"
  635.            ,rc
  636.            ,BMPlenrgbtable(class, cBitCount));
  637.   }
  638.  
  639.   pixels = BMPreadbits(ifp, &pos, offBits, cx, cy
  640.                ,cBitCount, class, R, G, B);
  641.  
  642.   if (pos != BMPlenfile(class, cBitCount, cx, cy)) {
  643.     pm_message("warning: read %d bytes, expected to read %d bytes"
  644.            ,pos
  645.            ,BMPlenfile(class, cBitCount, cx, cy));
  646.   }
  647.  
  648.   pm_close(ifp);
  649.   ppm_writeppm(stdout, pixels, cx, cy, (pixval) (MAXCOLORS - 1), 0);
  650.   pm_close(stdout);
  651.  
  652.   exit(0);
  653. }
  654.  
  655. /*\
  656.  * $Id: ppmtobmp.c,v 1.9 1992/11/24 19:39:33 dws Exp dws $
  657.  *
  658.  * ppmtobmp.c - Converts from a PPM file to a Microsoft Windows or OS/2
  659.  * .BMP file.
  660.  *
  661.  * The current implementation is probably not complete, but it works for
  662.  * me.  I welcome feedback.
  663.  *
  664.  * Copyright (C) 1992 by David W. Sanderson.
  665.  *
  666.  * Permission to use, copy, modify, and distribute this software and its
  667.  * documentation for any purpose and without fee is hereby granted,
  668.  * provided that the above copyright notice appear in all copies and
  669.  * that both that copyright notice and this permission notice appear
  670.  * in supporting documentation.  This software is provided "as is"
  671.  * without express or implied warranty.
  672.  *
  673.  * $Log: ppmtobmp.c,v $
  674.  * Revision 1.9  1992/11/24  19:39:33  dws
  675.  * Added copyright.
  676.  *
  677.  * Revision 1.8  1992/11/17  02:16:52  dws
  678.  * Moved length functions to bmp.h.
  679.  *
  680.  * Revision 1.7  1992/11/11  23:18:16  dws
  681.  * Modified to adjust the bits per pixel to 1, 4, or 8.
  682.  *
  683.  * Revision 1.6  1992/11/11  22:43:39  dws
  684.  * Commented out a superfluous message.
  685.  *
  686.  * Revision 1.5  1992/11/11  05:58:06  dws
  687.  * First version that works.
  688.  *
  689.  * Revision 1.4  1992/11/11  03:40:32  dws
  690.  * Moved calculation of bits per pixel to BMPEncode.
  691.  *
  692.  * Revision 1.3  1992/11/11  03:02:34  dws
  693.  * Added BMPEncode function.
  694.  *
  695.  * Revision 1.2  1992/11/08  01:44:35  dws
  696.  * Added option processing and reading of PPM file.
  697.  *
  698.  * Revision 1.1  1992/11/08  00:46:07  dws
  699.  * Initial revision
  700.  \*/
  701.  
  702. #define MAXCOLORS 256
  703.  
  704. /*
  705.  * Utilities
  706.  */
  707.  
  708. static char er_write[] = "stdout: write error";
  709.  
  710. /* prototypes */
  711. static void PutByte ARGS((FILE * fp, char v));
  712. static void PutShort ARGS((FILE * fp, short v));
  713. static void PutLong ARGS((FILE * fp, long v));
  714. static int BMPwritefileheader ARGS((FILE * fp, int class, unsigned long bitcount,
  715.                     unsigned long x, unsigned long y));
  716. static int BMPwriteinfoheader ARGS((FILE * fp, int class, unsigned long bitcount,
  717.                     unsigned long x, unsigned long y));
  718. static int BMPwritergb ARGS((FILE * fp, int class, pixval R, pixval G, pixval B));
  719. static int BMPwritergbtable ARGS((FILE * fp, int class, int bpp, int colors,
  720.                   pixval * R, pixval * G, pixval * B));
  721. static int BMPwriterow ARGS((FILE * fp, pixel * row, unsigned long cx,
  722.                  unsigned short bpp, colorhash_table cht));
  723. static int BMPwritebits ARGS((FILE * fp, unsigned long cx, unsigned long cy,
  724.          unsigned short cBitCount, pixel ** pixels, colorhash_table cht));
  725. static int colorstobpp ARGS((int colors));
  726. static void BMPEncode ARGS((FILE * fp, int class, int x, int y, pixel ** pixels,
  727.     int colors, colorhash_table cht, pixval * R, pixval * G, pixval * B));
  728. static void PutByte(fp, v)
  729.     FILE *fp;
  730.     char v;
  731. {
  732.   if (putc(v, fp) == EOF) {
  733.     pm_error(er_write);
  734.   }
  735. }
  736.  
  737. static void PutShort(fp, v)
  738.     FILE *fp;
  739.     short v;
  740. {
  741.   if (pm_writelittleshort(fp, v) == -1) {
  742.     pm_error(er_write);
  743.   }
  744. }
  745.  
  746. static void PutLong(fp, v)
  747.     FILE *fp;
  748.     long v;
  749. {
  750.   if (pm_writelittlelong(fp, v) == -1) {
  751.     pm_error(er_write);
  752.   }
  753. }
  754.  
  755. /*
  756.  * BMP writing
  757.  */
  758.  
  759. /*
  760.  * returns the number of bytes written, or -1 on error.
  761.  */
  762. static int BMPwritefileheader(fp, class, bitcount, x, y)
  763.     FILE *fp;
  764.     int class;
  765.     unsigned long bitcount;
  766.     unsigned long x;
  767.     unsigned long y;
  768. {
  769.   PutByte(fp, 'B');
  770.   PutByte(fp, 'M');
  771.  
  772.   /* cbSize */
  773.   PutLong(fp, BMPlenfile(class, bitcount, x, y));
  774.  
  775.   /* xHotSpot */
  776.   PutShort(fp, 0);
  777.  
  778.   /* yHotSpot */
  779.   PutShort(fp, 0);
  780.  
  781.   /* offBits */
  782.   PutLong(fp, BMPoffbits(class, bitcount));
  783.  
  784.   return 14;
  785. }
  786.  
  787. /*
  788.  * returns the number of bytes written, or -1 on error.
  789.  */
  790. static int BMPwriteinfoheader(fp, class, bitcount, x, y)
  791.     FILE *fp;
  792.     int class;
  793.     unsigned long bitcount;
  794.     unsigned long x;
  795.     unsigned long y;
  796. {
  797.   long cbFix;
  798.  
  799.   /* cbFix */
  800.   switch (class) {
  801.     case C_WIN:
  802.       cbFix = 40;
  803.       PutLong(fp, cbFix);
  804.  
  805.       /* cx */
  806.       PutLong(fp, x);
  807.       /* cy */
  808.       PutLong(fp, y);
  809.       /* cPlanes */
  810.       PutShort(fp, 1);
  811.       /* cBitCount */
  812.       PutShort(fp, bitcount);
  813.  
  814.       /*
  815.        * We've written 16 bytes so far, need to write 24 more
  816.        * for the required total of 40.
  817.        */
  818.  
  819.       PutLong(fp, 0);
  820.       PutLong(fp, 0);
  821.       PutLong(fp, 0);
  822.       PutLong(fp, 0);
  823.       PutLong(fp, 0);
  824.       PutLong(fp, 0);
  825.  
  826.       break;
  827.     case C_OS2:
  828.       cbFix = 12;
  829.       PutLong(fp, cbFix);
  830.  
  831.       /* cx */
  832.       PutShort(fp, x);
  833.       /* cy */
  834.       PutShort(fp, y);
  835.       /* cPlanes */
  836.       PutShort(fp, 1);
  837.       /* cBitCount */
  838.       PutShort(fp, bitcount);
  839.  
  840.       break;
  841.     default:
  842.       pm_error(er_internal, "BMPwriteinfoheader");
  843.   }
  844.  
  845.   return cbFix;
  846. }
  847.  
  848. /*
  849.  * returns the number of bytes written, or -1 on error.
  850.  */
  851. static int BMPwritergb(fp, class, R, G, B)
  852.     FILE *fp;
  853.     int class;
  854.     pixval R;
  855.     pixval G;
  856.     pixval B;
  857. {
  858.   switch (class) {
  859.     case C_WIN:
  860.       PutByte(fp, B);
  861.       PutByte(fp, G);
  862.       PutByte(fp, R);
  863.       PutByte(fp, 0);
  864.       return 4;
  865.     case C_OS2:
  866.       PutByte(fp, B);
  867.       PutByte(fp, G);
  868.       PutByte(fp, R);
  869.       return 3;
  870.     default:
  871.       pm_error(er_internal, "BMPwritergb");
  872.   }
  873.   return -1;
  874. }
  875.  
  876. /*
  877.  * returns the number of bytes written, or -1 on error.
  878.  */
  879. static int BMPwritergbtable(fp, class, bpp, colors, R, G, B)
  880.     FILE *fp;
  881.     int class;
  882.     int bpp;
  883.     int colors;
  884.     pixval *R;
  885.     pixval *G;
  886.     pixval *B;
  887. {
  888.   int nbyte = 0;
  889.   int i;
  890.   long ncolors;
  891.  
  892.   for (i = 0; i < colors; i++) {
  893.     nbyte += BMPwritergb(fp, class, R[i], G[i], B[i]);
  894.   }
  895.  
  896.   ncolors = (1 << bpp);
  897.  
  898.   for (; i < ncolors; i++) {
  899.     nbyte += BMPwritergb(fp, class, 0, 0, 0);
  900.   }
  901.  
  902.   return nbyte;
  903. }
  904.  
  905. /*
  906.  * returns the number of bytes written, or -1 on error.
  907.  */
  908. static int BMPwriterow(fp, row, cx, bpp, cht)
  909.     FILE *fp;
  910.     pixel *row;
  911.     unsigned long cx;
  912.     unsigned short bpp;
  913.     colorhash_table cht;
  914. {
  915.   BITSTREAM b;
  916.   unsigned nbyte = 0;
  917.   int rc;
  918.   unsigned x;
  919.  
  920.   if ((b = pm_bitinit(fp, "w")) == (BITSTREAM) 0) {
  921.     return -1;
  922.   }
  923.  
  924.   for (x = 0; x < cx; x++, row++) {
  925.     if ((rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, row))) == -1) {
  926.       return -1;
  927.     }
  928.     nbyte += rc;
  929.   }
  930.  
  931.   if ((rc = pm_bitfini(b)) == -1) {
  932.     return -1;
  933.   }
  934.   nbyte += rc;
  935.  
  936.   /*
  937.    * Make sure we write a multiple of 4 bytes.
  938.    */
  939.   while (nbyte % 4) {
  940.     PutByte(fp, 0);
  941.     nbyte++;
  942.   }
  943.  
  944.   return nbyte;
  945. }
  946.  
  947. /*
  948.  * returns the number of bytes written, or -1 on error.
  949.  */
  950. static int BMPwritebits(fp, cx, cy, cBitCount, pixels, cht)
  951.     FILE *fp;
  952.     unsigned long cx;
  953.     unsigned long cy;
  954.     unsigned short cBitCount;
  955.     pixel **pixels;
  956.     colorhash_table cht;
  957. {
  958.   int nbyte = 0;
  959.   long y;
  960.  
  961.   if (cBitCount > 24) {
  962.     pm_error("cannot handle cBitCount: %d"
  963.          ,cBitCount);
  964.   }
  965.  
  966.   /*
  967.    * The picture is stored bottom line first, top line last
  968.    */
  969.  
  970.   for (y = cy - 1; y >= 0; y--) {
  971.     int rc;
  972.  
  973.     rc = BMPwriterow(fp, pixels[y], cx, cBitCount, cht);
  974.  
  975.     if (rc == -1) {
  976.       pm_error("couldn't write row %d"
  977.            ,y);
  978.     }
  979.     if (rc % 4) {
  980.       pm_error("row had bad number of bytes: %d"
  981.            ,rc);
  982.     }
  983.     nbyte += rc;
  984.   }
  985.  
  986.   return nbyte;
  987. }
  988.  
  989. /*
  990.  * Return the number of bits per pixel required to represent the
  991.  * given number of colors.
  992.  */
  993.  
  994. static int colorstobpp(colors)
  995.     int colors;
  996. {
  997.   int bpp;
  998.  
  999.   if (colors < 1) {
  1000.     pm_error("can't have less than one color");
  1001.   }
  1002.  
  1003.   if ((bpp = pm_maxvaltobits(colors - 1)) > 8) {
  1004.     pm_error("can't happen");
  1005.   }
  1006.  
  1007.   return bpp;
  1008. }
  1009.  
  1010. /*
  1011.  * Write a BMP file of the given class.
  1012.  *
  1013.  * Note that we must have 'colors' in order to know exactly how many
  1014.  * colors are in the R, G, B, arrays.  Entries beyond those in the
  1015.  * arrays are undefined.
  1016.  */
  1017. static void BMPEncode(fp, class, x, y, pixels, colors, cht, R, G, B)
  1018.     FILE *fp;
  1019.     int class;
  1020.     int x;
  1021.     int y;
  1022.     pixel **pixels;
  1023.     int colors;                            /* number of valid entries in R,G,B */
  1024.     colorhash_table cht;
  1025.     pixval *R;
  1026.     pixval *G;
  1027.     pixval *B;
  1028. {
  1029.   int bpp;                            /* bits per pixel */
  1030.   unsigned long nbyte = 0;
  1031.  
  1032.   bpp = colorstobpp(colors);
  1033.  
  1034.   /*
  1035.    * I have found empirically at least one BMP-displaying program
  1036.    * that can't deal with (for instance) using 3 bits per pixel.
  1037.    * I have seen no programs that can deal with using 3 bits per
  1038.    * pixel.  I have seen programs which can deal with 1, 4, and
  1039.    * 8 bits per pixel.
  1040.    *
  1041.    * Based on this, I adjust actual the number of bits per pixel
  1042.    * as follows.  If anyone knows better, PLEASE tell me!
  1043.    */
  1044.   switch (bpp) {
  1045.     case 2:
  1046.     case 3:
  1047.       bpp = 4;
  1048.       break;
  1049.     case 5:
  1050.     case 6:
  1051.     case 7:
  1052.       bpp = 8;
  1053.       break;
  1054.   }
  1055.  
  1056.   pm_message("Using %d bits per pixel", bpp);
  1057.  
  1058.   nbyte += BMPwritefileheader(fp, class, bpp, x, y);
  1059.   nbyte += BMPwriteinfoheader(fp, class, bpp, x, y);
  1060.   nbyte += BMPwritergbtable(fp, class, bpp, colors, R, G, B);
  1061.  
  1062.   if (nbyte != (BMPlenfileheader(class)
  1063.         + BMPleninfoheader(class)
  1064.         + BMPlenrgbtable(class, bpp))) {
  1065.     pm_error(er_internal, "BMPEncode");
  1066.   }
  1067.  
  1068.   nbyte += BMPwritebits(fp, x, y, bpp, pixels, cht);
  1069.   if (nbyte != BMPlenfile(class, bpp, x, y)) {
  1070.     pm_error(er_internal, "BMPEncode");
  1071.   }
  1072. }
  1073.  
  1074. int main(argc, argv)
  1075.     int argc;
  1076.     char **argv;
  1077. {
  1078.   FILE *ifp = stdin;
  1079.   char *usage = "[-windows] [-os2] [ppmfile]";
  1080.   int class = C_OS2;
  1081.  
  1082.   int argn;
  1083.   int rows;
  1084.   int cols;
  1085.   int colors;
  1086.   int i;
  1087.   pixval maxval;
  1088.   colorhist_vector chv;
  1089.   pixval Red[MAXCOLORS];
  1090.   pixval Green[MAXCOLORS];
  1091.   pixval Blue[MAXCOLORS];
  1092.  
  1093.   pixel **pixels;
  1094.   colorhash_table cht;
  1095.  
  1096.   ppm_init(&argc, argv);
  1097.  
  1098.   argn = 1;
  1099.  
  1100.   while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
  1101.     if (pm_keymatch(argv[argn], "-windows", 2))
  1102.       class = C_WIN;
  1103.     else if (pm_keymatch(argv[argn], "-os2", 2))
  1104.       class = C_OS2;
  1105.     else
  1106.       pm_usage(usage);
  1107.     ++argn;
  1108.   }
  1109.  
  1110.   if (argn < argc) {
  1111.     ifp = pm_openr(argv[argn]);
  1112.     ++argn;
  1113.   }
  1114.  
  1115.   if (argn != argc) {
  1116.     pm_usage(usage);
  1117.   }
  1118.  
  1119.   pixels = ppm_readppm(ifp, &cols, &rows, &maxval);
  1120.  
  1121.   pm_close(ifp);
  1122.  
  1123.   /* Figure out the colormap. */
  1124.   pm_message("computing colormap...");
  1125.   chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
  1126.   if (chv == (colorhist_vector) 0)
  1127.     pm_error("too many colors - try doing a 'ppmquant %d'"
  1128.          ,MAXCOLORS);
  1129.   pm_message("%d colors found", colors);
  1130.  
  1131.   /*
  1132.    * Now turn the ppm colormap into the appropriate GIF
  1133.    * colormap.
  1134.    */
  1135.   if (maxval > 255) {
  1136.     pm_message("maxval is not 255 - automatically rescaling colors");
  1137.   }
  1138.   for (i = 0; i < colors; ++i) {
  1139.     if (maxval == 255) {
  1140.       Red[i] = PPM_GETR(chv[i].color);
  1141.       Green[i] = PPM_GETG(chv[i].color);
  1142.       Blue[i] = PPM_GETB(chv[i].color);
  1143.     }
  1144.     else {
  1145.       Red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / maxval;
  1146.       Green[i] = (pixval) PPM_GETG(chv[i].color) * 255 / maxval;
  1147.       Blue[i] = (pixval) PPM_GETB(chv[i].color) * 255 / maxval;
  1148.     }
  1149.   }
  1150.  
  1151.   /* And make a hash table for fast lookup. */
  1152.   cht = ppm_colorhisttocolorhash(chv, colors);
  1153.   ppm_freecolorhist(chv);
  1154.  
  1155.   /* All set, let's do it. */
  1156.   BMPEncode(stdout, class
  1157.         ,cols, rows, pixels, colors, cht
  1158.         ,Red, Green, Blue);
  1159.  
  1160.   pm_close(stdout);
  1161.  
  1162.   exit(0);
  1163. }
  1164.